﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using Storage;
using Storage.EntityModels;
using System.Data.Linq;
using Stats.Objects;

namespace Stats
{
    /// <summary>
    /// This class contains the methods to calculates the user savings such as
    /// the emissions saved, the fuel liters saved and the money saved using our TripLOA system
    /// </summary>
    public class AuxSavingsMethods
    {
        #region FuelSavings Methods

        /// <summary>
        /// calculate the amount of fuel saved in a carpooled trip, 
        /// </summary>
        /// <param name="tripID">the id of the trip</param>
        /// <returns>the fuel saved in a single trip</returns>
        public static double getFuelSavedInTrip(int tripID)
        {
            double? totalFuelSavedInTrip = 0;
            double defaultUserFuelConsumption = 0;

            try
            {
                TripLoaDataContext db = StorageManager.Instance.Entities;

                var tripStageEntries =
                    from stageEntry in db.Stages
                    where stageEntry.TripId == tripID
                    select stageEntry;

                double stageKm = 0;

                foreach (var tripStageEntry in tripStageEntries)
                {
                    var psnEntries =
                        from psnEntry in db.Users_Stages
                        where psnEntry.Stage.TripId == tripID && psnEntry.Stage.Ordinal == tripStageEntry.Ordinal
                        select new { PsnID = psnEntry.UserId, RequestedSeats = psnEntry.RequestedSeats };

                    stageKm = AuxDistanceMethods.getStageKm(tripStageEntry.Id);

                    foreach (var psnEntry in psnEntries)
                    {
                        var usrCarFuelConsumption =
                            (from usrEntry in db.Users
                             where usrEntry.Id == psnEntry.PsnID
                             select usrEntry.CarFuelConsumption).First();

                        // controllo ridondante..di default a un user gli viene assegnato per le sue macchine un consumo medio

                        if (usrCarFuelConsumption == 0 || usrCarFuelConsumption < 0)
                        {
                            usrCarFuelConsumption = (float) StatsModule.UtilitaryCarsFuelConsumptionReference;
                            StatsModule.setUserVehicleType(psnEntry.PsnID, (int) StatsModule.vehicleTypes.UtilitaryCarVehicleType);
                        }

                        totalFuelSavedInTrip += (stageKm / usrCarFuelConsumption) * psnEntry.RequestedSeats;

                    }

                    var stageInfo =
                        (from stages in db.Stages
                         where stages.Id == tripStageEntry.Id
                         select new { StageVehicle = stages.Users_VehicleDetails}).First();

                    if (stageInfo.StageVehicle.VehicleDetails.VehicleClass != 0)
                    {
                        defaultUserFuelConsumption = (double) StatsModule.UtilitaryCarsFuelConsumptionReference;

                        totalFuelSavedInTrip += (stageKm / defaultUserFuelConsumption);
                    }

                }

            }
            catch (InvalidOperationException) { totalFuelSavedInTrip = 0; }
            catch (NullReferenceException) { totalFuelSavedInTrip = 0; }
            catch (System.Data.SqlClient.SqlException) { totalFuelSavedInTrip = 0; }

            return (double)totalFuelSavedInTrip;

        }

        /// <summary>
        /// calculates the amount of fuel saved by a single user in a single trip
        /// </summary>
        /// <param name="stageID">the Id of the stage</param>
        /// <returns>the fuel saved</returns>
        public static double getFuelSavedInStagePerUser(int stageID)
        {
            double? fuelLitersSavedPerUser = 0;
            double? allPsnFuelLitersSaved = 0;
            double defaultUserFuelConsumption = 0;

            int numUsersInStage = 1; /* DRIVER */
            int passengersInEntry = 0;

            double stageKm = 0;

            try
            {
                TripLoaDataContext db = StorageManager.Instance.Entities;

                var psnEntries =
                       from psnEntry in db.Users_Stages
                       where psnEntry.StageId == stageID
                       select new { PsnID = psnEntry.UserId, RequestedSeats = psnEntry.RequestedSeats };

                stageKm = AuxDistanceMethods.getStageKm(stageID);

                foreach (var psnEntry in psnEntries)
                {
                    passengersInEntry = psnEntry.RequestedSeats;
                    numUsersInStage += passengersInEntry;

                    var usrCarFuelConsumption =
                         (from usrEntry in db.Users
                          where usrEntry.Id == psnEntry.PsnID
                          select usrEntry.CarFuelConsumption).First();

                    // controllo ridondante..di default a un user gli viene assegnato per le sue macchine un consumo medio sicuramente != 0

                    if (usrCarFuelConsumption == 0 || usrCarFuelConsumption < 0)
                    {
                        usrCarFuelConsumption = (float) StatsModule.UtilitaryCarsFuelConsumptionReference;
                        StatsModule.setUserVehicleType(psnEntry.PsnID, (int) StatsModule.vehicleTypes.UtilitaryCarVehicleType);
                    }

                    allPsnFuelLitersSaved += (stageKm / usrCarFuelConsumption) * passengersInEntry;

                }

                var stageInfo =
                    (from stages in db.Stages
                     where stages.Id == stageID
                     select new { StageVehicle = stages.Users_VehicleDetails}).First();

                if (stageInfo.StageVehicle.VehicleDetails.VehicleClass != 0)
                {
                    defaultUserFuelConsumption = (float) StatsModule.UtilitaryCarsFuelConsumptionReference;
                    
                    allPsnFuelLitersSaved += (stageKm / defaultUserFuelConsumption);
                }

                fuelLitersSavedPerUser = allPsnFuelLitersSaved / numUsersInStage;

            }
            catch (InvalidOperationException) { fuelLitersSavedPerUser = 0; }
            catch (NullReferenceException) { fuelLitersSavedPerUser = 0; }
            catch (System.Data.SqlClient.SqlException) { fuelLitersSavedPerUser = 0; }

            return (double) fuelLitersSavedPerUser;

        }

        #endregion

        #region EmissionsSavings Methods

        /// <summary>
        /// calculates the emissions saved per trip
        /// </summary>
        /// <param name="tripID">tripID</param>
        /// <returns>grams of CO2 saved doing this car-pooling trip</returns>
        public static double getEmissionsSavedInTrip(int tripID)
        {
            double? totalEmissionsSavedInTrip = 0;
            double stageKm = 0;
            double defaultUserCo2Emissions = 0;

            try
            {
                TripLoaDataContext db = StorageManager.Instance.Entities;

                var tripStageEntries =
                    from stageEntry in db.Stages
                    where stageEntry.TripId == tripID
                    select stageEntry;

                foreach (var tripStageEntry in tripStageEntries)
                {
                    var psnEntries =
                        from psnEntry in db.Users_Stages
                        where psnEntry.Stage.TripId == tripID && psnEntry.Stage.Ordinal == tripStageEntry.Ordinal
                        select new { PsnID = psnEntry.UserId, RequestedSeats = psnEntry.RequestedSeats };

                    stageKm = AuxDistanceMethods.getStageKm(tripStageEntry.Id);

                    foreach (var psnEntry in psnEntries)
                    {
                        var usrCarEmissions =
                             (from usrEntry in db.Users
                              where usrEntry.Id == psnEntry.PsnID
                              select usrEntry.CarCo2Emissions).First();

                        // controllo ridondante..di default a un user gli viene assegnato per le sue macchine un valore di emissioni di Co2 medio

                        if (usrCarEmissions == 0 || usrCarEmissions < 0)
                        {
                            usrCarEmissions = (int) StatsModule.UtilitaryCarsEmissionsReference;
                            StatsModule.setUserVehicleType(psnEntry.PsnID, (int)StatsModule.vehicleTypes.UtilitaryCarVehicleType);
                        }
                       
                        totalEmissionsSavedInTrip += ((usrCarEmissions * stageKm / StatsModule.EmissionMeasureDivisionUnit) * psnEntry.RequestedSeats);

                    }

                    var stageInfo =
                    (from stages in db.Stages
                     where stages.Id == tripStageEntry.Id
                     select new { StageVehicle = stages.Users_VehicleDetails}).First();

                    if (stageInfo.StageVehicle.VehicleDetails.VehicleClass != 0)
                    {
                        defaultUserCo2Emissions = (int) StatsModule.UtilitaryCarsEmissionsReference;

                        totalEmissionsSavedInTrip += (stageKm * defaultUserCo2Emissions / StatsModule.EmissionMeasureDivisionUnit);

                    }

                }

            }
            catch (InvalidOperationException) { totalEmissionsSavedInTrip = 0; }
            catch (NullReferenceException) { totalEmissionsSavedInTrip = 0;}
            catch (System.Data.SqlClient.SqlException) { totalEmissionsSavedInTrip = 0; }

            return (double) totalEmissionsSavedInTrip;

        }

        /// <summary>
        /// calculates the emissions saved in a single stage per user
        /// </summary>
        /// <param name="stageID">the Id of the stage</param>
        /// <returns>quintals of Co2 saved for each user in this stage</returns>
        public static double getEmissionsSavedInStagePerUser(int stageID)
        {
            double? emissionsSavedPerUser = 0;
            double? allPassengersEmissionsSavedInStage = 0;

            int numUsersInStage = 1; /* THE DRIVER */
            int passengersInEntry = 0;
            
            double stageKm = 0;
            double defaultUserCo2Emissions = 0;

            try
            {
                TripLoaDataContext db = StorageManager.Instance.Entities;

                var psnEntries =
                       from psnEntry in db.Users_Stages
                       where psnEntry.StageId == stageID
                       select new { PsnID = psnEntry.UserId, RequestedSeats = psnEntry.RequestedSeats };

                stageKm = AuxDistanceMethods.getStageKm(stageID);

                foreach (var psnEntry in psnEntries)
                {
                    passengersInEntry = psnEntry.RequestedSeats;
                    numUsersInStage += passengersInEntry;

                    var usrCarEmissions =
                         (from usrEntry in db.Users
                          where usrEntry.Id == psnEntry.PsnID
                          select usrEntry.CarCo2Emissions).First();

                    // controllo ridondante..di default a un user gli viene assegnato per le sue macchine un valore di emissioni di Co2 medio

                    if (usrCarEmissions == 0 || usrCarEmissions < 0)
                    {
                        usrCarEmissions = (int) StatsModule.UtilitaryCarsEmissionsReference;
                        StatsModule.setUserVehicleType(psnEntry.PsnID, (int)StatsModule.vehicleTypes.UtilitaryCarVehicleType);
                    }
                  
                    allPassengersEmissionsSavedInStage += (((usrCarEmissions * stageKm) / StatsModule.EmissionMeasureDivisionUnit)* passengersInEntry);
                }

                var stageInfo =
                    (from stages in db.Stages
                     where stages.Id == stageID
                     select new {StageVehicle = stages.Users_VehicleDetails }).First();

                if (stageInfo.StageVehicle.VehicleDetails.VehicleClass != 0)
                {
                    defaultUserCo2Emissions = (int) StatsModule.UtilitaryCarsEmissionsReference;

                    allPassengersEmissionsSavedInStage += ((stageKm * defaultUserCo2Emissions) / StatsModule.EmissionMeasureDivisionUnit);
                
                }

                emissionsSavedPerUser = (allPassengersEmissionsSavedInStage / numUsersInStage);

            }
            catch (InvalidOperationException) { emissionsSavedPerUser = 0; }
            catch (NullReferenceException) { emissionsSavedPerUser = 0; }
            catch (System.Data.SqlClient.SqlException) { emissionsSavedPerUser = 0; }

            // emissions are returned in quintals of Co2 (unitMeasureDivision in StatsModule constants)

            return (double)emissionsSavedPerUser;

        }

        #endregion

        /// <summary>
        /// calculates all the savings of a trip (emissions, fuel, etc...) and save it in a TripSavings object
        /// </summary>
        /// <param name="tripID">the trip id</param>
        /// <returns>a TripSavings object</returns>
        public static TripSavings getTripSavings(int tripID)
        {
            double? totalFuelSavedInTrip = 0;
            double? totalEmissionsSavedInTrip = 0;
            double stageKm = 0;

            double userCarEmissions = 0;
            double userCarFuelConsumption = 0;

            try
            {
                TripLoaDataContext db = StorageManager.Instance.Entities;

                var tripStageEntries =
                    from stageEntry in db.Stages
                    where stageEntry.TripId == tripID
                    select stageEntry;

                foreach (var tripStageEntry in tripStageEntries)
                {
                    var psnEntries =
                        from psnEntry in db.Users_Stages
                        where psnEntry.Stage.TripId == tripID && psnEntry.Stage.Ordinal == tripStageEntry.Ordinal
                        select new { PsnID = psnEntry.UserId, RequestedSeats = psnEntry.RequestedSeats };

                    stageKm = AuxDistanceMethods.getStageKm(tripStageEntry.Id);

                    foreach (var psnEntry in psnEntries)
                    {
                        var usrCarDetails =
                             (from usrEntry in db.Users
                             where usrEntry.Id == psnEntry.PsnID
                             select new { FuelConsumption = usrEntry.CarFuelConsumption, Co2Emissions = usrEntry.CarCo2Emissions }).First();

                        userCarEmissions = usrCarDetails.Co2Emissions;
                        userCarFuelConsumption = usrCarDetails.FuelConsumption;

                        // controllo ridondante..di default a un user gli viene assegnato per le sue macchine un consumo medio sicuramente !=0 

                        if (usrCarDetails.FuelConsumption == 0 || usrCarDetails.FuelConsumption < 0 || usrCarDetails.Co2Emissions == 0 || usrCarDetails.Co2Emissions < 0)
                        {
                            userCarFuelConsumption = (float) StatsModule.UtilitaryCarsFuelConsumptionReference;
                            userCarEmissions = (int) StatsModule.UtilitaryCarsEmissionsReference;
                            StatsModule.setUserVehicleType(psnEntry.PsnID, (int)StatsModule.vehicleTypes.UtilitaryCarVehicleType);
                        }

                        totalFuelSavedInTrip += (stageKm / userCarFuelConsumption) * psnEntry.RequestedSeats;
                        totalEmissionsSavedInTrip += ((userCarEmissions * stageKm / StatsModule.EmissionMeasureDivisionUnit) * psnEntry.RequestedSeats);

                    }

                    var stageInfo =
                    (from stages in db.Stages
                     where stages.Id == tripStageEntry.Id
                     select new { StageVehicle = stages.Users_VehicleDetails }).First();

                    if (stageInfo.StageVehicle.VehicleDetails.VehicleClass != 0)
                    {
                        userCarFuelConsumption = (float) StatsModule.UtilitaryCarsFuelConsumptionReference;
                        userCarEmissions = (int) StatsModule.UtilitaryCarsEmissionsReference;
                        
                        totalFuelSavedInTrip += (stageKm / userCarFuelConsumption);
                        totalEmissionsSavedInTrip += (stageKm * userCarEmissions / StatsModule.EmissionMeasureDivisionUnit);
                    }
                }

            }
            catch (InvalidOperationException) { totalFuelSavedInTrip = 0; totalEmissionsSavedInTrip = 0; }
            catch (NullReferenceException) { totalFuelSavedInTrip = 0; totalEmissionsSavedInTrip = 0; }
            catch (System.Data.SqlClient.SqlException) { totalFuelSavedInTrip = 0; totalEmissionsSavedInTrip = 0; }

            /*emissions are returned as quintals of Co2 as specified in EmissionsMeasureUnitDivision in StatsModule*/

            return new TripSavings(tripID, (double)totalFuelSavedInTrip, (double)totalEmissionsSavedInTrip, getMoneySaved((double)totalFuelSavedInTrip));
        }

        #region MoneySavings Methods

        /// <summary>
        /// returns the money saved in a trip
        /// </summary>
        /// <param name="tripID">the trip id</param>
        /// <returns>the money saved</returns>
        public static double getMoneySavedInTrip(int tripID)
        {
            return (getFuelSavedInTrip(tripID) * StatsModule.FuelCostPerLiterReference);
        }

        /// <summary>
        /// returns the money that a single user save in a single stage using tripLoa
        /// </summary>
        /// <param name="stageID">the id of the stage</param>
        /// <returns>the money saved</returns>
        public static double getMoneySavedInStagePerUser(int stageID)
        {
            return getFuelSavedInStagePerUser(stageID) * StatsModule.FuelCostPerLiterReference;
        }

        /// <summary>
        /// returns the money saved saving a certain amount of fuel
        /// </summary>
        /// <param name="fuelSaved">the amount of fuel saved</param>
        /// <returns>the money saved</returns>
        public static double getMoneySaved(double fuelSaved)
        {
            return fuelSaved * StatsModule.FuelCostPerLiterReference;
        }

        #endregion

    }
}
